This is a race.
This bug is caused by multi-threaded accessing to the non-thread-safe
class
java.util.regex.Matcher at
groovy.servlet.AbstractHttpServlet#applyResourceNameMatcher at:
matcher.reset(uri); (groovy.servlet.AbstractHttpServlet 280)
Two exceptions may be thrown out by running the test:
Exception in thread "Thread-6" java.lang.IndexOutOfBoundsException:
start 14, end 13, s.length() 31
at
java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:470)
at java.lang.StringBuffer.append(StringBuffer.java:313)
at java.util.regex.Matcher.appendReplacement(Matcher.java:756)
at
java.util.regex.Matcher.replaceAll(Matcher.java:823)
at
groovy.servlet.AbstractHttpServlet.applyResourceNameMatcher(AbstractHttpServlet.java:283)
at
groovy.servlet.AbstractHttpServlet.getScriptUri(AbstractHttpServlet.java:251)
at groovy.servlet.Groovy6456$TestThread.run(Groovy6456.java:77)
Exception in thread "Thread-1" java.lang.IllegalStateException: No match available
at java.util.regex.Matcher.appendReplacement(Matcher.java:692)
at java.util.regex.Matcher.replaceAll(Matcher.java:813)
at groovy.servlet.AbstractHttpServlet.applyResourceNameMatcher(AbstractHttpServlet.java:283)
at groovy.servlet.AbstractHttpServlet.getScriptUri(AbstractHttpServlet.java:251)
at groovy.servlet.Groovy6456$TestThread.run(Groovy6456.java:90)
Numbers in following code snippets are program global execution sequence. And number in brackets present an alternative interleaving to get another exception.
groovy.servlet.AbstractHttpServlet:
t1 t2 private String applyResourceNameMatcher(final String aUri) {
...
1 3(4) matcher.reset(uri);
String replaced;
if (resourceNameReplaceAll) {
2 replaced = matcher.replaceAll(resourceNameReplacement);
}
...
}
java.util.regex.Matcher:
public Matcher appendReplacement(StringBuffer sb, String replacement) {
// If no match, return error
4(3) if (first < 0)
throw new IllegalStateException("No match available");
...
(5) sb.append(getSubSequence(lastAppendPosition, first));
...
}
a)
thread 1 resets matcher at 1.
b) thread 1 calls replaceall at 2 and comes to 4.
c) before thread 1 executes 4, context switched, thread 2 comes to 3.
d) thread 2 resets matcher at 3 and first at 4 is a negative number now.
e) thread 1 executes 4, and throws IllegalStateException.
Or alternatively,
a)
thread 1 resets matcher at 1.
b) thread 1 calls replaceall at 2.
c) thread 1 executes (3),and comes to (5)
d) before thread 1 executes (5), context switched, thread 2 executes (4) first.
e) now first is set to a negative value in step d), thread 1 executes (5), an IndexOutOfBoundsException is thrown.
This bug is reproduced under groovy 1.7.9 and JDK 1.6.0_33.
Execute
the following scripts to run the test to reproduce the bug (assume the
location of the groovy test project is groovy_test_home).
Linux:
${groovy_test_home}/scripts/run6456.sh [--threadnum arg]
Windows:
%groovy_test_home%\scripts\run6456.bat [-threadnum arg]
The default argument values will be taken if not specified. For
example,
${groovy_test_home}/scripts/run6456.sh
is the same as
${groovy_test_home}/scripts/run6456.sh -threadnum 50
| Option | Function | Default Value | Valid Values |
| --threadnum,-tn | thread number | 50 | integer |